export default class UtilsEntity {
  linkPath = '.';
  constructor(linkPath = '.') {
    this.linkPath = linkPath;
  }

  // 转换成大驼峰
  toUpperCase(name: string) {
    return name
      .split('-')
      .map((str) => str.slice(0, 1).toUpperCase() + str.slice(1))
      .join('');
  }
  // 首字母小写
  toLowerCase(name: string) {
    return name.slice(0, 1).toLowerCase() + name.slice(1);
  }
  // 驼峰转换下划线
  toLine(name: string) {
    return name.replace(/([A-Z])/g, '_$1').toLowerCase();
  }

  changePropName(propName = ''): string {
    return (propName || '').split('.').join(this.linkPath);
  }

  toOriginPropName(propName = ''): string {
    return (propName || '').split(this.linkPath).join('.');
  }

  pathToObject(path: string, defaultValue: any) {
    // 分割并倒序
    const arr = path
      .split(this.linkPath)
      .reduce((total: string[], key: string) => {
        const matchRes = key.match(/\[\d+\]/g);
        const isArr = !!matchRes;
        if (isArr && matchRes) {
          const name = key.replace(matchRes.join(''), '');
          if (name.trim()) {
            total.push(name);
          }
          total = total.concat(matchRes);
        } else {
          total.push(key);
        }
        return total;
      }, [])
      .reverse();
    // 获取初始化的数据结构
    function getInitData(key: string): { [key: string]: any } {
      return key.match(/\[\d+\]/g) ? [] : {};
    }
    // 获取正确的key名
    function getRightKey(key: string) {
      const matchRes = key.match(/\[\d+\]/g);
      const isArr = !!matchRes;
      const name = matchRes ? matchRes[0].match(/\d+/g) : key;
      return isArr && name && name[0] ? name[0] : key;
    }
    const firstKey = arr.shift();
    // 创建一个新的对象用来接受
    const data = getInitData(firstKey || '');
    // 获取正确的key名
    const rightFirstKey = getRightKey(firstKey || '');
    // 给予默认值
    data[rightFirstKey] = defaultValue;
    // 获取结果值
    return arr.reduce(function (
      obj: { [key: string]: any },
      key: string,
      index: number
    ) {
      // 将当前的key值进行初始化出来一个对象
      const currentKey = getRightKey(key);
      // 创建一个新的对象用来接受
      const newObj = getInitData(key);
      newObj[currentKey] = obj;
      // 返回结果
      return newObj;
    },
    data);
  }

  // getType(obj: any): string;
  // getType(obj: any, str: string): boolean;
  getType(obj: any, str?: string) {
    const typeStr = Object.prototype.toString.call(obj).split(' ')[1];
    const type = typeStr
      .substring(0, typeStr.length - 1)
      .trim()
      .toLowerCase();
    if (
      type === 'object' &&
      Object.prototype.toString.call(obj).toLowerCase() === '[object object]' &&
      !obj.length
    ) {
      return str ? 'json' === str : 'json';
    }
    return str ? type === str : type;
  }

  isPromise(obj: any) {
    // return this.getType(fn, 'asyncfunction') as Boolean
    return (
      !!obj && //有实际含义的变量才执行方法，变量null，undefined和''空串都为false
      (typeof obj === 'object' || typeof obj === 'function') && // 初始promise 或 promise.then返回的
      typeof obj.then === 'function'
    );
  }

  deepClone(data: any) {
    const type = this.getType(data);
    return Object.keys(data).reduce(
      (total: any, key: string | number) => {
        key = type === 'array' ? +key : key;
        if (
          this.getType(data[key], 'json') &&
          data[key].constructor &&
          data[key].constructor.name !== 'Object'
        ) {
          total[key] = data[key];
        } else {
          total[key] =
            ['array', 'json'].indexOf(this.getType(data[key]) as string) < 0
              ? data[key]
              : this.deepClone(data[key]);
        }
        return total;
      },
      type === 'array' ? [] : {}
    );
  }

  deepMerge(baseObj: { [key: string]: any }, newObj: { [key: string]: any }) {
    return Object.keys(newObj || {}).reduce((total, key) => {
      if (key in total) {
        const newType = this.getType(newObj[key]) as string;
        const baseType = this.getType(total[key]);
        if (newType === baseType) {
          total[key] =
            ['json', 'array'].indexOf(newType) < 0
              ? newObj[key]
              : this.deepMerge(baseObj[key], newObj[key]);
        } else {
          total[key] = newObj[key];
        }
      } else {
        total[key] = newObj[key];
      }
      return total;
    }, baseObj);
  }

  dataBump(data: { [key: string]: any }) {
    const keys = Object.keys(data).sort((a, b) => (a < b ? -1 : 1));
    const objArr = keys.map((str) => this.pathToObject(str, data[str]));
    const isArr = objArr.map((obj) => Array.isArray(obj)).every((b) => b);
    return objArr.reduce(
      (result, obj, index) => (!index ? obj : this.deepMerge(result, obj)),
      isArr ? [] : {}
    );
  }

  objToPath(obj: { [key: string | number]: any }, startKey = '') {
    startKey = startKey || '';
    const isArray = Array.isArray(obj);
    return Object.keys(obj).reduce((total: { [key: string]: any }, key) => {
      let column = `${startKey}${isArray ? '[' : '.'}${key}${
        isArray ? ']' : ''
      }`;
      if (column.indexOf('.') === 0) {
        column = column.substring(1, column.length);
      }
      column = this.changePropName(column);
      if (this.getType(obj[key], 'json') || this.getType(obj[key], 'array')) {
        total = Object.assign(total, this.objToPath(obj[key], column));
      } else {
        total[column] = obj[key];
      }
      return total;
    }, {});
  }

  isEqual(a: any, b: any): boolean {
    const AType = this.getType(a);
    const BType = this.getType(b);
    if (AType !== BType) {
      return false;
    }
    switch (AType) {
      case 'number':
      case 'string':
      case 'boolean':
      case 'date':
        return a === b;
      case 'regexp':
        return '' + a === '' + b;
      case 'array':
        if (a.length !== b.length) {
          return false;
        }
        const bool = a
          .map((ai: any, index: number) => this.isEqual(ai, b[index]))
          .every((bool: boolean) => bool);
        return bool;
      case 'json':
        const aKeys = Object.keys(a);
        if (aKeys.length !== Object.keys(b).length) {
          return false;
        }
        const bool2 = aKeys
          .map((k: string) => {
            if (!(k in b)) {
              return false;
            }
            return this.isEqual(a[k], b[k]);
          })
          .every((bool: boolean) => bool);
        return bool2;
      case 'function':
        return a.toString() === b.toString();
      case 'set':
        if (a.size !== b.size) {
          return false;
        }
        return this.isEqual([...a], [...b]);
    }

    return a === b;
  }

  uuid() {
    return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(
      /[xy]/g,
      function (c) {
        const r = (Math.random() * 16) | 0,
          v = c === 'x' ? r : (r & 0x3) | 0x8;
        return v.toString(16);
      }
    );
  }
}
